//********************************************************************************************************
// Product Name: MapWindow.dll Alpha
// Description:  The basic module for MapWindow version 6.0
//********************************************************************************************************
// The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); 
// you may not use this file except in compliance with the License. You may obtain a copy of the License at 
// http://www.mozilla.org/MPL/ 
//
// Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF 
// ANY KIND, either expressed or implied. See the License for the specificlanguage governing rights and 
// limitations under the License. 
//
// The Original Code is from MapWindow.dll version 6.0
//
// The Initial Developer of this Original Code is Ted Dunsford. Created 6/12/2008 2:47:04 PM
// 
// Contributor(s): (Open source contributors should list themselves and their modifications here). 
//
//********************************************************************************************************

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using MapWindow;
using MapWindow.Main;
using MapWindow.Data;
using MapWindow.Drawing;
using MapWindow.Geometries;
using Microsoft.DirectX.Direct3D;
namespace MapWindow.DirectX
{


    /// <summary>
    /// TileDX
    /// </summary>
    public class TileDX : RenderBaseDX
    {

        #region Events

        /// <summary>
        /// Textures load in the background.  This represents the event where the true texture
        /// has finally loaded.
        /// </summary>
        public event EventHandler TextureLoaded;

        #endregion

        #region Private Variables

        ITextureLayerDX _texture;

        int _level;
        int _lat;
        int _lon;
        bool _exists;
        const int NORTHWEST = 0;
        const int NORTHEAST = 1;
        const int SOUTHEAST = 2;
        const int SOUTHWEST = 3;
        TileDX[] _subTiles;
     
        
        string _filename;

        private Envelope _extents;
        private ImageDX _missingImage;
        private ImageDX _loadingImage;

        /// <summary>
        /// An Image is a rectangle with a texture, but generally depicts a smaller
        /// portion of the source texture, controled by the floating point bounds.
        /// </summary>
        public ImageDX MissingImage
        {
            get
            {
                return _missingImage;
            }
            set
            {
                _missingImage = value;
            }
        }

        /// <summary>
        /// An Image is a rectangle with a texture, but generally depicts a smaller
        /// portion of the source texture, controled by the floating point bounds.
        /// </summary>
        public ImageDX LoadingImage
        {
            get
            {
                return _loadingImage;
            }
            set
            {
                _loadingImage = value; 
            }
        }

        
        private TileDX _parent;
        

        /// <summary>
        /// Gets or sets whether the texture for this tile has been loaded yet.
        /// </summary>
        public bool IsLoaded
        {
            get {
                if (_texture == null) return false;
                return _texture.IsLoaded; 
            }
           
        }

      

        #endregion

        #region Constructors

        /// <summary>
        /// Creates a new instance of TileDX
        /// </summary>
        public TileDX(string directory, int level, int lat, int lon, TileDX parent)
        {

            _parent = parent;
            _level = level;
            _lat = lat;
            _lon = lon;
            _filename = directory + "\\" + level.ToString() + "\\" + lat.ToString("0000") + "\\" + lat.ToString("0000") + "_" + lon.ToString("0000") + ".dds";
            _extents = GetEnvelope(_level, _lat, _lon);
            if (System.IO.File.Exists(_filename))
            {
                _exists = true;
            
                _subTiles = new TileDX[4];

                _subTiles[NORTHWEST] = new TileDX(directory, level + 1, lat * 2, lon * 2 + 1, this);
                _subTiles[NORTHWEST].TextureLoaded += new EventHandler(TileDX_TextureLoaded);
                
                _subTiles[NORTHEAST] = new TileDX(directory, level + 1, lat * 2 + 1, lon * 2 + 1, this);
                _subTiles[NORTHEAST].TextureLoaded += new EventHandler(TileDX_TextureLoaded);

                _subTiles[SOUTHWEST] = new TileDX(directory, level + 1, lat * 2, lon * 2, this);
                _subTiles[SOUTHWEST].TextureLoaded += new EventHandler(TileDX_TextureLoaded);

                _subTiles[SOUTHEAST] = new TileDX(directory, level + 1, lat * 2 + 1, lon * 2, this);
                _subTiles[SOUTHEAST].TextureLoaded += new EventHandler(TileDX_TextureLoaded);



               
            }
           
          
           
        }

        /// <summary>
        /// When a subtile loads, this passes the event along to the drawing process to redraw
        /// the map if necessary.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void TileDX_TextureLoaded(object sender, EventArgs e)
        {
            OnTextureLoaded(sender, e);
        }

        /// <summary>
        /// 0: TopLeft, 1:TopRight, 2:BottomLeft, 3:BottomRight
        /// </summary>
        public Quadrants Position
        {
            get
            {
                if (_lat % 2 == 0)
                {
                    if (_lon % 2 == 0)
                    {
                        return Quadrants.SouthWest;

                    }
                    else
                    {
                        return Quadrants.SouthEast;
                    }
                }
                else
                {
                    if (_lon % 2 == 0)
                    {
                        return Quadrants.NorthWest;

                    }
                    else
                    {
                        return Quadrants.NorthEast;
                    }

                }
            }

        }

        /// <summary>
        /// Gets the Microsoft.DirectX.Direct3D.Texture
        /// </summary>
        public Texture Texture
        {
            get { return _texture.Texture; }
            protected set { _texture.Texture = value; }

        }
       

       

        /// <summary>
        /// Instructs the tile to start loading and creates a low-resolution stand in where 
        /// the parent textures don't exist.
        /// </summary>
        public override void Initialize(DrawArgsDX args)
        {
            WorldWindFileProviderDX tileLoader = new WorldWindFileProviderDX();
            Texture parentTexture;
            if (_parent != null)
            {
                if (_parent.Exists && _parent.IsLoaded )
                {
                    parentTexture = _parent.Texture;
                    _loadingImage = new ImageDX(Color.Green, _extents, _parent.Texture, Position);
                    _missingImage = new ImageDX(Color.Red, _extents, _parent.Texture, Position); 
                }
                else 
                {
                    // Loading is started by the drawing request, which may start at higher levels.
                    if(_parent.IsInitialized == false)_parent.Initialize(args);
                   
                    if (_parent.Exists)
                    {
                        _loadingImage = _parent.LoadingImage.ChildImage(Color.Green, Position);
                        _missingImage = _parent.LoadingImage.ChildImage(Color.Red, Position);
                    }
                    else
                    {
                        _loadingImage = _parent.MissingImage.ChildImage(Color.Green, Position);
                        _missingImage = _parent.MissingImage.ChildImage(Color.Red, Position);

                    }
                    
                    

                }   
               
            }
            else
            {
                _loadingImage = new ImageDX(Color.Green, _extents);
                _missingImage = new ImageDX(Color.Red, _extents);
            }

            if (_exists)
            {
                _texture = (ITextureLayerDX)tileLoader.OpenLayer(_filename, true, null, null);
                _texture.TextureLoaded += new EventHandler(_texture_TextureLoaded);
                
            }
         
             
            IsInitialized = true;
        }

       

       
        void _texture_TextureLoaded(object sender, EventArgs e)
        {
            OnTextureLoaded(sender, e);
        }

       

        #endregion

        #region Methods

        #endregion

        #region Properties

        /// <summary>
        /// Boolean true if there is a file in the specified path
        /// </summary>
        public bool Exists
        {
            get { return _exists; }
        }

        

        #endregion




        #region IRenderableDX Members

       
        /// <summary>
        /// Disposes the unmanaged memory components
        /// </summary>
        protected override void Dispose(bool disposing)
        {
            //if (components != null && disposing)
            //{
            //    components.Dispose();
            //}
            if(_loadingImage != null)_loadingImage.Dispose();
            if(_missingImage != null)_missingImage.Dispose();
            if(_texture != null)_texture.Dispose();
            if (_subTiles != null)
            {
                foreach (TileDX tile in _subTiles)
                {
                    if (tile.Exists)
                    {
                        tile.Dispose();
                    }
                }
            }
        }

        /// <summary>
        /// Closes this tile so that it is not taking up ram
        /// </summary>
        public void Close()
        {
            if (_level > 0)
            {
                if (_loadingImage != null) _loadingImage.Close();
                if (_missingImage != null) _missingImage.Close();
                if (_texture != null) _texture.Close();
                IsInitialized = false;
            }
            
            // Close each of the children
            if (_subTiles != null)
            {
                foreach (TileDX tile in _subTiles)
                {
                    tile.Close();
                    
                }
            }
           
            
            
        }

        /// <summary>
        /// This is the internal drawing method that follows the standard checks.  Drawing is evoked
        /// through the Draw3D command, but internally implemented in the DrawPart method.  After
        /// the initial checks, the Draw3D method will cycle the part integer from 0 to NumParts -1.
        /// </summary>
        /// <param name="args">A DrawArgsDX class with the device and camera.</param>
        protected override void OnDraw(DrawArgsDX args)
        {
          
            // Determine our approximate zoom level for determining if we should draw subtiles
            int z = Convert.ToInt32(Math.Abs(args.Camera.CameraLocation.Z));
            int level = 0;
            if (z < 90) level = 1;
            if (z < 45) level = 2;
            if (z < 20) level = 3;
            if (z < 10) level = 4;
            
            if (level < _level) return;

           

            
            

            // If the camera is closer than the zoom level for this tile, 
            // check the sub-level tiles and draw them.  Only draw this tile
            // in the event that one of the sub-level tiles isn't present.
            if (level > _level && _subTiles != null)
            {

                DrawSubtiles(args);
                return;
            }
            
            // In the final case, we are directly overhead, but zoomed out
            DrawTile(args);

        }

        private void DrawSubtiles(DrawArgsDX args)
        {

            Envelope target = new Envelope();
            double w = Math.Abs(args.Camera.View.Length());
            double x = Convert.ToDouble(args.Camera.TargetLocation.X);
            double y = Convert.ToDouble(args.Camera.TargetLocation.Y);

            target.SetExtents(x - w / 2, y - w / 2, x + w / 2, y + w / 2);

            // Test the extents here.
            if (target.Intersects(_extents))
            {
                foreach (TileDX tile in _subTiles)
                {
                    tile.Draw3D(args);
                }
            }
            else
            {
                DrawTile(args);
            }
        }

        // draw this tile
        private void DrawTile(DrawArgsDX args)
        {
            
            if (_exists)
            {
                if (_texture.IsInitialized == false)
                {
                    // Start loading the texture if we haven't started loading it yet.
                    _texture.Initialize(args);
                }

                if (_texture.IsLoaded)
                {
                    // Draw the detailed texture if it is loaded
                    _texture.Draw3D(args);
                }
                else
                {
                    // Draw the loading rectangle if it isn't loaded yet.
                    _loadingImage.Draw3D(args);
                }

                // We are at the correct drawing level, so draw this tile


            }
            else
            {

                _missingImage.Draw3D(args);
            }
        }

        /// <summary>
        /// An Envelope defining the geographic bounds of this texture
        /// </summary>
        public override IEnvelope Envelope
        {
            get 
            {
              
                if (_texture != null) return _texture.Envelope;
                return new Envelope();
            }
        }

       

        private Envelope GetEnvelope(int level, int lat, int lon)
        {
            double degSize = 36;
            if (level> 0) degSize = (double)degSize / Math.Pow(2, level);
            Envelope env = new Envelope();
            double x = degSize * lon - 180;
            double y = degSize * lat - 90;
            env.SetExtents(x, y, x + degSize, y + degSize);
            return env;
        }


        

        #endregion


        #region Protected Methods

        /// <summary>
        /// Raises the TextureLoaded Event
        /// </summary>
        /// <param name="sender">The object triggering the event</param>
        /// <param name="e">A System.EventArgs</param>
        protected virtual void OnTextureLoaded(object sender, EventArgs e)
        {
            if (TextureLoaded != null)
            {
                TextureLoaded(sender, e);
            }
        }

       

       

       
        #endregion
    }
}
